////////////////////////////////////////////////////////////////////////////////
//
// (C) Andy Thomason 2011 
//	Conversion by Petros Mizamidis
//
// Box2D sample

#ifdef WIN32
  #define _CRT_SECURE_NO_WARNINGS 1
  // This is the "GL extension wrangler"
  // Note: you will need to use the right version of glew32s.lib (32 bit vs 64 bit)
  #define GLEW_STATIC
  #include "GL/glew.h"

  // This is the "GL utilities" framework for drawing with OpenGL
  #define FREEGLUT_STATIC
  #define FREEGLUT_LIB_PRAGMAS 0
  #include "GL/glut.h"
  #include "AL/al.h"
  #include "AL/alc.h"
#else
  // please advise for Mac settings:
  #include "GLUT/glut.h"
  #include "AL/al.h"
  #include "AL/alc.h"
#endif

#include <list>
#include <stdio.h>
#include <math.h>
#include <assert.h>

#include <vector.h>
#include <matrix.h>

#include <shader.h>

#include <file_manager.h>
#include <texture_manager.h>
#include <sound_manager.h>

#include <Box2D/Box2D.h>



// modified box class from previous wraps a box2d object
class box {
  b2Body *body;
  b2World *world;
  GLuint texture_;
  b2PolygonShape shape;
  float umax_;
   
public:
  box() { body = 0; }

  void init(b2World &world, float angle, float cx, float cy, float hx, float hy, float density, float umax, GLuint texture) {
    b2BodyDef def;
    def.position.Set(cx, cy);
    def.angle = angle;
  	def.type = density ? b2_dynamicBody : b2_staticBody;
    body = world.CreateBody(&def);
    shape.SetAsBox(hx, hy);
    b2FixtureDef fixture;
    fixture.density = density;
    fixture.friction = 0.1f;
	fixture.restitution = 1.1;
    fixture.shape = &shape;
    body->CreateFixture(&fixture);
    texture_ = texture;
    umax_ = umax;
	 }
  
//  void DestroyBox () {
//	  world->DestroyBody(body);	  
//		}
  

  void draw(shader &shader) {
    if (!body) {
      return;
    }
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture_);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	

    // set the uniforms
    shader.render();
    
		b2Vec2 position = body->GetPosition();
		float32 angle = body->GetAngle();
		float cx = position.x;
		float cy = position.y;
		
		int count = shape.GetVertexCount();
		float vertices[4*4];
		float *p = vertices;
		float c = cosf(angle);
		float s = sinf(angle);
		assert(count == 4);
		static const float u[] = { 0, 1, 1, 0 };
		static const float v[] = { 0, 0, 1, 1 };
		for (int i = 0; i != count; ++i) {
		  b2Vec2 vtx = shape.GetVertex(i);
		  float x = position.x + vtx.x * c - vtx.y * s;
		  float y = position.y + vtx.x * s + vtx.y * c;
		  p[0] = x;
		  p[1] = y;
		  p[2] = u[i] * umax_;
		  p[3] = v[i];
		  p += 4;
		}

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)vertices );
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)(vertices + 2) );
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(2);

    // kick the draw
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	
  }
};


// main game class: one instance only
class angry_bricks : public b2ContactListener
{
  int counter;
  shader texture_shader_;
  GLint viewport_width_;
  GLint viewport_height_;
  ALuint buffers[256];
  bool switch_;
  char keys[256];
  char prev_keys[256];
  b2World box2d_world;
  
  enum { num_boxes = 10 };
  box boxes[num_boxes];
  
  void draw_world(shader &shader) {
    for (int i = 0; i != num_boxes; ++i) {
		boxes[i].draw(texture_shader_);
    }
	
  }

  void simulate() {
    memcpy(prev_keys, keys, sizeof(prev_keys));
    box2d_world.Step(1.0f/60, 6, 2);
	if (counter==200)
		exit(0);

	if (counter==10){
	GLuint brick = texture_manager::new_texture("assets/textures.tga", 128, 128, 128, 128);	
	boxes[2].init(box2d_world, 1.3f, 2.0f, 5.0f, 0.5, 0.5, 0.5, 1, brick);
	}

	if (counter==30){
	GLuint brick = texture_manager::new_texture("assets/textures.tga", 128, 128, 128, 128);	
	boxes[3].init(box2d_world, 0.9f, 3.0f, 5.0f, 0.5, 0.9, 1, 1, brick);	
	}

	if (counter==50){
	GLuint brick = texture_manager::new_texture("assets/textures.tga", 128, 128, 128, 128);	
	boxes[4].init(box2d_world, 1.9f, 4.0f, 5.0f, 1, 2, 1.5, 1, brick);
	}


  }
  
  void render() {
    simulate();
	

    // clear the frame buffer and the depth
    glClearColor(0, 0, 1, 1);
    glViewport(0, 0, viewport_width_, viewport_height_);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    
    draw_world(texture_shader_);
	drawString(-0.9f,0.9f,0);
	

    // swap buffers so that the image is displayed.
    // gets a new buffer to draw a new scene.
    glutSwapBuffers();
  }
  
  // event called every time we begin a contact
  void BeginContact(b2Contact* contact) {
    switch_=true;
	int sound = 1;
    char name[2];
    name[0] = sound + '0';
    name[1] = 0;
    sound_manager::play(vec4(0, 0, 0, 0), name);
	counter++;
		
  }
 
  	void drawString(float x, float y, float z)
	{ 

		glEnable(GL_COLOR_MATERIAL);
		glColor3f(0.8f, 0.2f, 0.0f);
		glRasterPos3f(x, y, z);
		char counterArr[300];
		sprintf_s(counterArr,"Hits:" "%d", counter );

		for (const char* c=counterArr; *c != '\0'; c++)
		{
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c); 
		}
		

	}
  

  angry_bricks() : box2d_world(b2Vec2(0, -10))
  {
    box2d_world.SetContactListener(this);
    memset(keys, 0, sizeof(keys));
    memset(prev_keys, 0, sizeof(prev_keys));
	char textures[3];
	switch_=false;
	
    sound_manager::add_buffers("assets/labels.txt", "assets/bricks.wav");
    
    // set up a simple shader to render the emissve color
    texture_shader_.init(
      // just copy the position attribute to gl_Position
      "varying vec2 uv_;"
      "attribute vec3 pos;"
      "attribute vec2 uv;"
      "void main() { gl_Position = vec4(pos * 0.05f, 1); uv_ = uv; }",

      // just copy the color attribute to gl_FragColor
      "varying vec2 uv_;"
	  "uniform sampler2D texture;"
      "void main() { gl_FragColor = texture2D(texture, uv_); }"
      //"void main() { gl_FragColor = vec4(1.0f, 1, 0.05f, 1); }"
    );

	
		GLuint brick = texture_manager::new_texture("assets/textures.tga", 128, 128, 128, 128);	
		GLuint wall = texture_manager::new_texture("assets/textures.tga", 0, 128, 128, 128);
	
        boxes[1].init(box2d_world, 1.9, 2.0f, 5.0f, 0.5, 0.5, 1, 1, brick);
		boxes[10].init(box2d_world,  0, 0.5f, 10.5f, 10.0f, 0.5f, 0, 4, wall);
		boxes[11].init(box2d_world, 0, -0.5f, -10.5f, 10.0f, 0.5f, 0, 4, wall );
		boxes[12].init(box2d_world, 1.575f, -10.5f, 0.5f, 10.0f, 0.5f, 0, 4, wall );
		boxes[13].init(box2d_world, 1.575f, 10.5f, -0.5f, 10.0f, 0.5f, 0, 4, wall );
	
  }
  
  
  // The viewport defines the drawing area in the window
  void set_viewport(int w, int h) {
    viewport_width_ = w;
    viewport_height_ = h;
  }
  void set_key(int key, int value) {
    keys[key & 0xff] = value;
  }
public:
  // a singleton: one instance of this class only!
  static angry_bricks &get()
  {
    static angry_bricks singleton;
    return singleton;
  }

  // interface from GLUT
  static void reshape(int w, int h) { get().set_viewport(w, h); }
  static void display() { get().render(); }
  static void timer(int value) { glutTimerFunc(30, timer, 1); glutPostRedisplay(); }
  static void key_down( unsigned char key, int x, int y) { get().set_key(key, 1); }
  static void key_up( unsigned char key, int x, int y) { get().set_key(key, 0); }
};

// boilerplate to run the sample
int main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
  glutInitWindowSize(600, 600);
  glutCreateWindow("angry bricks");
  #ifdef WIN32
    glewInit();
    if (!glewIsSupported("GL_VERSION_2_0") )
    {
      printf("OpenGL 2 is required!\n");
      return 1;
    }
  #endif
  glutDisplayFunc(angry_bricks::display);
  glutReshapeFunc(angry_bricks::reshape);
  glutKeyboardFunc(angry_bricks::key_down);
  glutKeyboardUpFunc(angry_bricks::key_up);
  glutTimerFunc(30, angry_bricks::timer, 1);
  glutMainLoop();
  return 0;
}

